package apollo

import (
	"encoding/json"
	"fmt"
	"gitee.com/dennis-kk/service-box-go/util/config/source"
	"gitee.com/dennis-kk/service-box-go/util/errors"
	"gitee.com/dennis-kk/service-box-go/util/tools"
	"net/url"
	"os"
	"path"
	"time"
)

const (
	defaultContentKey = "content"
)

type (
	// responseData 请求返回包结构
	responseData struct {
		AppID          string                 `json:"appId"`
		Cluster        string                 `json:"cluster"`
		NamespaceName  string                 `json:"namespaceName"`
		ReleaseKey     string                 `json:"releaseKey"`
		Configurations map[string]interface{} `json:"configurations"`
	}

	// 配置
	configData []byte

	apolloCenter struct {
		cfg *Config
	}
)

func (a *apolloCenter) Read() (*source.ChangeSet, error) {
	// 检查配置是否合法
	if a.cfg == nil || len(a.cfg.Host) == 0 || len(a.cfg.AppId) == 0 {
		return nil, errors.InvalidApolloCfg
	}
	// 构造请求
	api := a.getSyncApi(a.cfg.AppId, a.cfg.NameSpace, a.cfg.Cluster)
	requestURL := fmt.Sprintf("%s%s", a.cfg.Host, api)

	// TODO 返回结构里，支持多类型的配置
	cs := &source.ChangeSet{
		Format:    "yaml",
		Source:    a.String(),
		Timestamp: time.Now(),
	}

	cb := &CallBack{
		SuccessCallBack: a.onSuccess,
		Namespace:       a.cfg.NameSpace,
	}

	var err error
	cs.Data, err = Request(requestURL, a.cfg, cb)
	if err != nil {
		return nil, err
	}

	// 后处理，替换环境变量
	cs.Data = []byte(os.ExpandEnv(string(cs.Data)))

	return cs, nil
}

func (a *apolloCenter) String() string {
	return "apollo"
}

//getSyncApi 返回同步请求的apollo api
func (a *apolloCenter) getSyncApi(appId, namespace, cluster string) string {
	return fmt.Sprintf("/configs/%s/%s/%s?releaseKey=&ip=",
		url.QueryEscape(appId),
		url.QueryEscape(cluster),
		url.QueryEscape(namespace),
	)
}

func (a *apolloCenter) onSuccess(b []byte, callback CallBack) (data configData, err error) {
	// 返回包
	resp := &responseData{}
	resp.Configurations = make(map[string]interface{}, 0)
	err = json.Unmarshal(b, &resp)
	if tools.IsNotNil(err) {
		return nil, err
	}
	// 获取namespace 扩展名
	npExt := path.Ext(callback.Namespace)
	// 检查是否在支持的扩展里，如果在获取配置的值
	var cfgData configData
	if _, ok := a.cfg.ConfigExt[npExt]; ok {
		if val, ok := resp.Configurations[defaultContentKey]; ok {
			cfgData = configData(val.(string))
		}
	} else {
		cfgData, _ = json.Marshal(&resp.Configurations)
	}
	return cfgData, nil
}

func NewSource(opts ...source.Option) source.Source {
	apollo := &apolloCenter{}
	options := source.NewOptions(opts...)
	cfg, ok := options.Context.Value(cfgKey{}).(*Config)
	if ok {
		cfg.Auth = &AuthSignature{}
		cfg.IsRetry = false
		cfg.ConfigExt = configFormat{
			".yaml": true,
		}
		apollo.cfg = cfg
	}
	return apollo
}
